﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Diagnostics;
using System.Threading;

namespace WP7iPhone
{
    public partial class Page1 : PhoneApplicationPage
    {
        // Constructor
        public Page1()
        {
            LoadPages();
            IsArranging = false;

            InitializeComponent();
            holdTimer = new Timer(holdTimerCallback, this, Timeout.Infinite, Timeout.Infinite);

            foreach (var page in PageCollection)
            {
                AppPageControl pageControl = new AppPageControl();
                pageControl.DataContext = this;
                pageControl.ItemsSource = page;
                appPanel.Children.Add(pageControl);
            }

            Dock.DataContext = this;
            Dock.ItemsSource = DockCollection;

            PhoneApplicationService.Current.Closing += new EventHandler<ClosingEventArgs>(ApplicationClosed);

        }

        /// <summary>
        /// Initializes the various pages of icons
        /// </summary>
        private void LoadPages()
        {
            //
            // See if our page collection is already stored in IsolatedStorage
            //
            if (System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Contains("icons"))
            {
                PageCollection = (IconCollection[])System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["icons"];
            }
            else
            {
                //
                // Load up the new defaults.
                //
                PageCollection = new IconCollection[2];
                PageCollection[0] = new IconCollection
            {
                new Icon("Messages", "/WP7iPhone;component/Icons/chat-blank-icon.png"),
                new Icon("Calendar", "/WP7iPhone;component/Icons/cal-blank-icon.png"),
                new Icon("Photos", "/WP7iPhone;component/Icons/wallpaper-icon.png","/LaunchTask.xaml?task=photos"),
                new Icon("Camera", "/WP7iPhone;component/Icons/photo-icon.png","/LaunchTask.xaml?task=camera"),
                new Icon("Stocks", "/WP7iPhone;component/Icons/graph-icon.png"),
                new Icon("Weather", "/WP7iPhone;component/Icons/weather-icon.png"),
                new Icon("Maps", "/WP7iPhone;component/Icons/map-icon.png"),
                new Icon("Utilities", "/WP7iPhone;component/Icons/Folder1-icon.png"),
                new Icon("App Store", "/WP7iPhone;component/Icons/appstore-icon.png","/LaunchTask.xaml?task=appstore"),
                new Icon("At Bat 2010", "/WP7iPhone;component/Icons/MLB-icon.png"),
                new Icon("Runkeeper", "/WP7iPhone;component/Icons/runkeeper-icon.png"),
                new Icon("Bing", "/WP7iPhone;component/Icons/bing-icon.png"),
                new Icon("Reader", "/WP7iPhone;component/Icons/reader-icon.png"),
                new Icon("Settings", "/WP7iPhone;component/Icons/settings-icon.png", "/Apps/Settings/Settings.xaml"),
                new Icon("Facebook", "/WP7iPhone;component/Icons/facebook-icon.png"),
                new Icon("Kindle", "/WP7iPhone;component/Icons/kindle-icon.png")
            };

                PageCollection[1] = new IconCollection {
                new Icon("Contacts", "/WP7iPhone;component/Icons/contact-icon.png","/LaunchTask.xaml?task=contacts"),
                new Icon("Bellevue Map", "/WP7iPhone;component/Icons/bellevue-map-icon.png"),
                new Icon("Family Calendar", "/WP7iPhone;component/Icons/familycalendar-icon.png"),
                new Icon("Amazon.com", "/WP7iPhone;component/Icons/amazon-icon.png"),
                new Icon("Dropbox", "/WP7iPhone;component/Icons/dropbox-icon.png"),
                new Icon("Epicurious", "/WP7iPhone;component/Icons/epicurious-icon.png"),
                new Icon("Travel", "/WP7iPhone;component/Icons/folder2-icon.png"),
                new Icon("Games", "/WP7iPhone;component/Icons/folder3-icon.png"),
                new Icon("YouTube", "/WP7iPhone;component/Icons/youtube-icon.png"),
                new Icon("Marvel", "/WP7iPhone;component/Icons/Marvel-icon.png"),
                new Icon("iTunes", "/WP7iPhone;component/Icons/itunes-icon.png","/LaunchTask.xaml?task=itunes"),
                new Icon("Pandora", "/WP7iPhone;component/Icons/pandora-icon.png"),
                new Icon("Notes", "/WP7iPhone;component/Icons/notes-icon.png"),
                new Icon("NYTimes", "/WP7iPhone;component/Icons/nytimes-icon.png"),
                new Icon("Bloomberg", "/WP7iPhone;component/Icons/bloomberg-icon.png"),
                new Icon("PhoneFlicks", "/WP7iPhone;component/Icons/phoneflix-icon.png")
            };
            }

            DockCollection = new IconCollection
            {
                new Icon("Phone","/WP7iPhone;component/Icons/phone-icon.png","/LaunchTask.xaml?task=phone"),
                new Icon("Mail","/WP7iPhone;component/Icons/mail-icon.png","/LaunchTask.xaml?task=mail"),
                new Icon("Safari", "/WP7iPhone;component/Icons/browser-icon.png","/LaunchTask.xaml?task=web"),
                new Icon("iPod", "/WP7iPhone;component/Icons/ipod-icon.png")
            };

        }

        public bool IsArranging
        {
            get { return (bool)GetValue(IsArrangingProperty); }
            set { SetValue(IsArrangingProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsArranging.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsArrangingProperty =
            DependencyProperty.Register("IsArranging", typeof(bool), typeof(Page1), null);

        

        /// <summary>
        /// When the user has finished scrolling the scroll view, we need to snap the left edge 
        /// to the nearest page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollViewer_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            //
            // Find the nearest x-coordinate multiple of 480 and scroll to that.
            //
            int targetX = ((int)(scrollView.HorizontalOffset + 240.0) / 480) * 480;

            snapAnimation.From = scrollView.HorizontalOffset;
            snapAnimation.To = targetX;

            snapStoryboard.Begin();
        }


        private Point mouseDownPos;
        private bool isHolding=false;
        private Timer holdTimer;
        private FrameworkElement heldIcon;
        private IconCollection[] PageCollection;
        private IconCollection DockCollection;

        /// <summary>
        /// Callback when the hold timer fires. This is responsible for calling the
        /// OnHold method on its dispatcher.
        /// </summary>
        /// <param name="state"></param>
        private static void holdTimerCallback(object state)
        {
            Page1 page = (Page1)state;
            page.Dispatcher.BeginInvoke(delegate() { page.OnHold(page.heldIcon); });
        }

        /// <summary>
        /// Method which gets called once we've detected a held down button. This is 
        /// responsible for starting/stopping the wobble animation.
        /// </summary>
        private void OnHold(FrameworkElement icon)
        {
            if (!isHolding)
            {
                return;
            }

            var elements = VisualTreeHelper.FindElementsInHostCoordinates(mouseDownPos, App.Current.RootVisual);

            IsArranging = !IsArranging;
            if (IsArranging)
            {

                //
                // If the user was holding over an icon, grow that icon now
                //
                AnimateScale(icon, 1.2);                   
                
                WobbleTrigger.Start();
            }
            else
            {
                //
                // If the user was holding over an icon, shrink that icon now
                //
                AnimateScale(icon, 1.0);

                WobbleTrigger.Stop();
            }

        }

        private void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {

            //
            // If we are not clicking on an app icon, do nothing
            //
            FrameworkElement source = (FrameworkElement)(e.OriginalSource);
            heldIcon = (FrameworkElement)source.GetVisualAncestorsAndSelf().Where(x => ((FrameworkElement)x).Name == "appIcon").FirstOrDefault();

            if (heldIcon != null)
            {
                isHolding = true;
                mouseDownPos = e.GetPosition(null);
                holdTimer.Change(1500, Timeout.Infinite);

                if (IsArranging)
                {
                    AnimateScale(heldIcon, 1.2);
                    heldIcon.CaptureMouse();
                }
               
            }

        }

        private void image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point currentPos = e.GetPosition(null);
            FrameworkElement source = (FrameworkElement)(e.OriginalSource);

            if (isHolding)
            {
                isHolding = false;
                holdTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }

            if (IsArranging)
            {
                heldIcon.ReleaseMouseCapture();
                AnimateScale(heldIcon, 1.0);
                RearrangeIcon(heldIcon, currentPos);
            }
            else
            {
                //
                // User has clicked on it, if the app is implemented, navigate to it
                //
                if (heldIcon != null)
                {
                    Icon i = (Icon)heldIcon.DataContext;
                    if (i.Uri != null)
                    {
                        NavigationService.Navigate(new Uri(i.Uri, UriKind.Relative));
                    }                   
                }

            }
        }

        /// <summary>
        /// Helper function which 'drops' an icon into a new position
        /// in the grid
        /// </summary>
        /// <param name="target">Supplies the icon to be dropped</param>
        /// <param name="newPos">Supplies the new position (drop location)</param>
        private void RearrangeIcon(FrameworkElement target, Point newPos)
        {
            AppPageControl sourcePage = (AppPageControl)target.GetVisualAncestors().Where(x => x is AppPageControl).First();
            WrapPanel sourcePanel = FindParentPanel(target);
            UIElement sourceContainer = (UIElement)sourcePage.ItemContainerGenerator.ContainerFromItem(target.DataContext);

            //
            // Find the new index where this icon should be inserted
            //
            // fixfix - currently this does not handle the case where the source
            //          and destination panels are different. You can't make that
            //          happen now because you can't drag between panels yet.
            //          Will also need to handle ripple-effect there where you 
            //          drag a new icon into a panel that is already full.
            //

            var elements = VisualTreeHelper.FindElementsInHostCoordinates(newPos, App.Current.RootVisual);
            foreach (var elem in elements)
            {
                FrameworkElement dest = elem as FrameworkElement;
                if ((dest != null) && (dest.Name == "appIcon") && (dest != target))
                {

                    //
                    // Ok, we've found a destination icon which is different from the target
                    // being dropped
                    //
                    AppPageControl destPage = (AppPageControl)dest.GetVisualAncestors().Where(x => x is AppPageControl).First();
                    WrapPanel destPanel = FindParentPanel(dest);
                    UIElement destContainer = (UIElement)destPage.ItemContainerGenerator.ContainerFromItem(dest.DataContext);

                    int sourceIndex = sourcePanel.Children.IndexOf(sourceContainer);
                    int destIndex   = destPanel.Children.IndexOf(destContainer);

                    if (destPanel == sourcePanel)
                    {
                        //
                        // We are rearranging within the same panel
                        //
                        UIElement e = sourcePanel.Children[sourceIndex];
                        sourcePanel.Children.RemoveAt(sourceIndex);
                        //
                        // Adjust the new position to account for the removal, if necessary
                        //
                        if (sourceIndex < destIndex)
                        {
                            --destIndex;
                        }
                        sourcePanel.Children.Insert(destIndex, e);

         
                    } else {
                    
                        //
                        // We are moving from one panel to another. This may involve overflowing the destination
                        // panel into a new page.
                        //
                        // fixfix implement this!
                        //
                        throw new NotImplementedException();
                    }
                    break;
                }
            }

            // fixfix - should animate this so the icon flies back home
            Grid g = (Grid)target.GetVisualChildren().Where(x => x is Grid).First();
            CompositeTransform transform = (CompositeTransform)g.RenderTransform;
            transform.TranslateX = 0;
            transform.TranslateY = 0;

        }

        /// <summary>
        /// A mouse movement will cancel the hold timer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void image_MouseMove(object sender, MouseEventArgs e)
        {
            Point currentPos = e.GetPosition(null);
            double deltaX = currentPos.X - mouseDownPos.X;
            double deltaY = currentPos.Y - mouseDownPos.Y;

            //
            // Even when we are holding the button down we get some small mousemove
            // events. So if the mouse hasn't moved very far, don't cancel the hold timer yet
            //
            double distance = (deltaX * deltaX) + (deltaY * deltaY);
            if (isHolding & distance > 20.0)
            {
                isHolding = false;
                holdTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }
            if (IsArranging)
            {
                Grid icon = (Grid)heldIcon.GetVisualChildren().Where(x => x is Grid).First(); ;

                CompositeTransform transform = (CompositeTransform)icon.RenderTransform;
                transform.TranslateX = deltaX;
                transform.TranslateY = deltaY;
            }


        }        

        static void AnimateScale(UIElement element, double scale)
        {
            Grid g = (Grid)element.GetVisualChildren().Where(x => x is Grid).First();

            // Create the storyboard.
            Storyboard sb = new Storyboard();
            Duration duration = new Duration(TimeSpan.FromSeconds(0.2));

            // Create a DoubleAnimation for the X and Y scaling
            DoubleAnimation animationX = new DoubleAnimation();
            animationX.Duration = duration;
            animationX.To = scale;
            sb.Children.Add(animationX);
            Storyboard.SetTarget(animationX, g);
            Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.ScaleX)"));

            DoubleAnimation animationY = new DoubleAnimation();
            animationY.Duration = duration;
            animationY.To = scale;
            sb.Children.Add(animationY);
            Storyboard.SetTarget(animationY, g);
            Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.ScaleY)"));

            sb.Begin();

        }

        private void StackPanel_Loaded(object sender, RoutedEventArgs e)
        {
            //
            // Scroll to the first app page so the search page is off-screen to the left
            //
            Mediator.HorizontalOffset = 480.0;
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            //
            // We want to find our respective object in the wrap panel and delete it
            //
            FrameworkElement element = (FrameworkElement)sender;

            Icon i = (Icon)element.DataContext;
            IconCollection coll = FindIconCollection(i);

            coll.Remove(i);

        }

        /// <summary>
        /// Manipulation delta callback which handles dragging the icon around the
        /// screen. We do this by simply setting the delta X and Y translation values into the 
        /// transform.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollViewer_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            if (IsArranging)
            {
                // fixfix we need some way to differentiate between someone moving an icon
                // and swiping to change pages. maybe the length of time between mouse down
                // and manipulationdelta?
                e.Handled = true;
            }
        }

        /// <summary>
        /// Given an icon, figures out which collection holds that icon
        /// </summary>
        /// <param name="icon"></param>
        /// <returns></returns>
        private IconCollection FindIconCollection(Icon icon)
        {
            foreach (var collection in PageCollection)
            {
                if (collection.Contains(icon))
                {
                    return collection;
                }
            }
            return null;
        }

        private WrapPanel FindParentPanel(FrameworkElement child)
        {
            DependencyObject parent = child;
            while (parent.GetType() != typeof(WrapPanel))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            return (WrapPanel)parent;
        }

        private void ApplicationClosed(object sender, ClosingEventArgs e)
        {
            // The application will not be tombstoned, so only save to Isolated Storage
            IconCollection[] pages = new IconCollection[appPanel.Children.Count - 1];
            for (int i = 1; i < appPanel.Children.Count; i++)
            {
                AppPageControl pageControl = (AppPageControl)appPanel.Children[i];
                var icons = pageControl.GetVisualDescendants()
                    .Where(x => ((FrameworkElement)x).Name == "appIcon")
                    .Select(x => (Icon)((FrameworkElement)x).DataContext);

                pages[i - 1] = new IconCollection(icons.ToList());
                
            }
            System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["icons"] = pages;
            System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();
        }

    }
}
